import java.util.Optional;
import java.util.stream.Collectors;

interface IOperation {
  String op(Player player, String opcode, IEntity entity);
}

public enum BuiltIn implements IOperation {
  LOOK {
    @Override
    public String op(Player player, String opcode, IEntity entity) {
      var cluster = player.getCurrentLocation();
      if (Optional.ofNullable(cluster).isEmpty()) {
        return "";
      }
      var sb = new StringBuilder();
      sb.append("You are at the ");
      sb.append(cluster.getName());
      sb.append(" - ");
      sb.append(cluster.getDescription());
      sb.append("\n");

      var furniture = Optional
          .ofNullable(cluster.getFurniture());
      var characters = Optional.ofNullable(cluster.getCharacters());
      var furniture1 = "";
      if (furniture.isPresent() && !furniture.get().isEmpty()) {
        furniture1 = furniture.get().stream()
            .map(Subgraph::getName)
            .collect(Collectors.joining(", "));
      }
      var characters1 = "";
      if (characters.isPresent() && !characters.get().isEmpty()) {
        characters1 = characters.get().stream()
            .map(Subgraph::getName)
            .collect(Collectors.joining(", "));
      }

      sb.append("You can also see: ");
      sb.append(furniture1);
      if (!furniture1.isEmpty() && !characters1.isEmpty()) {
        sb.append(", ");
      }
      sb.append(characters1);
      sb.append("\n");

      var artefacts = Optional
          .ofNullable(cluster.getArtefacts());
      sb.append("Items to pick up include: ");
      if (artefacts.isPresent() && !artefacts.get().isEmpty()) {
        var artefacts1 = artefacts.get().stream()
            .map(Subgraph::getName)
            .collect(Collectors.joining(", "));
        sb.append(artefacts1);
      }
      sb.append("\n");

      var reachablePaths = entity.reachableFrom(player.getCurrentLocation().getName());
      if (reachablePaths != null) {
        sb.append("There are paths to: ");
        sb.append(reachablePaths);
      }
      return sb.toString();
    }
  },

  INVENTORY {
    @Override
    public String op(Player player, String opcode, IEntity entity) {
      return "You currently have: " + player.getArtefacts().stream()
          .map(ArtefactItem::getName)
          .collect(Collectors.joining(", "));
    }
  },

  INV {
    @Override
    public String op(Player player, String opcode, IEntity entity) {
      return INVENTORY.op(player, opcode, entity);
    }
  },

  GET {
    @Override
    public String op(Player player, String opcode, IEntity entity) {
      var currentLocation = player.getCurrentLocation();
      var artefact = currentLocation.getArtefactBy(opcode);
      if (artefact.isPresent()) {
        var a = new ArtefactItem(artefact.get(), currentLocation);
        player.addArtefact(a);
        currentLocation.removeArtefactBy(artefact.get());
        return "You now have the " + opcode;
      } else {
        return "Can not get the " + opcode;
      }
    }
  },

  DROP {
    @Override
    public String op(Player player, String opcode, IEntity entity) {
      var dropped = player.removeArtefactBy(opcode);
      if (dropped.isPresent()) {
        var cluster = player.getCurrentLocation();
        cluster.addArtefact(dropped.get().artefact);
        return "You now have dropped the " + opcode;
      } else {
        return "Can not drop the " + opcode;
      }
    }
  },

  GOTO {
    @Override
    public String op(Player player, String opcode, IEntity entity) {
      var cluster = entity.getLocationBy(opcode);
      if (Optional.ofNullable(cluster).isPresent()) {
        player.setCurrentLocation(cluster);
        return LOOK.op(player, null, entity);
      }
      return "Can not to goto " + opcode;
    }
  },

  VIEW {
    @Override
    public String op(Player player, String opcode, IEntity entity) {
      return "You currently health: " + player.getHealth();
    }
  }

}
